/*
 *	ME2 C++ SDK
 *	Copyright (c) 2010 _silencer
 *
 *	Permission is hereby granted, free of charge, to any person
 *	obtaining a copy of this software and associated documentation
 *	files (the "Software"), to deal in the Software without
 *	restriction, including without limitation the rights to use,
 *	copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the
 *	Software is furnished to do so, subject to the following
 *	conditions:
 *
 *	The above copyright notice and this permission notice shall be
 *	included in all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *	OTHER DEALINGS IN THE SOFTWARE.
 */

#include "stdafx.h"
#include "unreal_sdk.h"

namespace unreal
{
	volatile process_event_type uengine_interface::process_event_real( reinterpret_cast<process_event_type>(OFFSET_UOBJECT_PROCESSEVENT) );

	void __declspec(naked) process_event_hook()
	{
		__asm
		{
			push ebp
			mov ebp, esp
			push [ebp+16]
			push [ebp+12]
			push [ebp+8]
			push ecx
			call uengine_interface::process_event_code
			pop ebp
			retn 12
		}
	}

	void __stdcall uengine_interface::process_event_code( uobject* caller, ufunction* function, void* parms, void* result )
	{
		uengine_interface& interface( uengine_interface::get_singleton() );

		uevent_listener_list_iterator listener_it( interface.event_listeners_.begin() );

		for ( ; listener_it != interface.event_listeners_.end(); ++listener_it )
		{
			if ( (*listener_it)->is_valid() && (*listener_it)->is_firing_before() )
			{
				(*listener_it)->on_unfiltered_event( caller, function, parms, result );

				if ( (*listener_it)->get_filter_on_caller() )
					(*listener_it)->on_filtered_caller_event( function, parms, result );

				if ( (*listener_it)->get_filter_on_function() )
					(*listener_it)->on_filtered_function_event( caller, parms, result );

				if ( (*listener_it)->get_filter_on_caller() && (*listener_it)->get_filter_on_function() )
					(*listener_it)->on_filtered_event(  parms, result );
			}
		}

		process_event_real( caller, function, parms, result );

		listener_it = interface.event_listeners_.begin();

		for ( ; listener_it != interface.event_listeners_.end(); ++listener_it )
		{
			if ( (*listener_it)->is_valid() && (*listener_it)->is_firing_after() )
			{
				(*listener_it)->on_unfiltered_event( caller, function, parms, result );

				if ( (*listener_it)->get_filter_on_caller() )
					(*listener_it)->on_filtered_caller_event( function, parms, result );

				if ( (*listener_it)->get_filter_on_function() )
					(*listener_it)->on_filtered_function_event( caller, parms, result );

				if ( (*listener_it)->get_filter_on_caller() && (*listener_it)->get_filter_on_function() )
					(*listener_it)->on_filtered_event(  parms, result );
			}
		}
	}

	void uengine_interface::initialize_interface()
	{
		if ( initialized_ )
			return;

		DWORD old_protect;

		::VirtualProtect( OFFSET_UOBJECT_PROCESSEVENT, 5, PAGE_EXECUTE_READWRITE, &old_protect );

		process_event_jmp_ = reinterpret_cast<uprocess_event_jmp*>(OFFSET_UOBJECT_PROCESSEVENT);
		process_event_trampoline_ = reinterpret_cast<uprocess_event_trampoline*>(::VirtualAlloc( 0, sizeof(uprocess_event_trampoline), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE ));

		if ( process_event_trampoline_ )
		{
			uint8* code_block( reinterpret_cast<uint8*>(OFFSET_UOBJECT_PROCESSEVENT) );

			std::copy( code_block, code_block + 5, process_event_trampoline_->origonal_code );
			process_event_trampoline_->jmp_cont_opcode = 0xE9;
			process_event_trampoline_->jmp_cont_address = reinterpret_cast<uint32>(OFFSET_UOBJECT_PROCESSEVENT) - reinterpret_cast<uint32>(process_event_trampoline_) - 5;

			process_event_jmp_->jmp_hook_opcode = 0xE9;
			process_event_jmp_->jmp_hook_address = ( reinterpret_cast<uint32>(&process_event_hook) - reinterpret_cast<uint32>(&process_event_jmp_->jmp_hook_opcode) ) - 5;

			process_event_real = reinterpret_cast<process_event_type>(process_event_trampoline_);

			initialized_ = true;
		}
		
		::VirtualProtect( OFFSET_UOBJECT_PROCESSEVENT, 5, old_protect, &old_protect );
	}

	void uengine_interface::cleanup_interface()
	{
		if ( !initialized_ )
			return;

		DWORD old_protect;

		::VirtualProtect( OFFSET_UOBJECT_PROCESSEVENT, 5, PAGE_EXECUTE_READWRITE, &old_protect );

		uint8* code_block( reinterpret_cast<uint8*>(OFFSET_UOBJECT_PROCESSEVENT) );

		std::copy( process_event_trampoline_->origonal_code, process_event_trampoline_->origonal_code + 5, code_block );

		::VirtualFree( process_event_trampoline_, 5, MEM_RELEASE );
		process_event_trampoline_= 0;

		process_event_jmp_ = 0;

		process_event_real = reinterpret_cast<process_event_type>(OFFSET_UOBJECT_PROCESSEVENT);

		::VirtualProtect( OFFSET_UOBJECT_PROCESSEVENT, 5, old_protect, &old_protect );
	}

	uevent_listener* uengine_interface::add_event_listener( uevent_listener* listener )
	{
		uevent_listener_list_iterator listener_it( std::find_if( event_listeners_.begin(), event_listeners_.end(), find_uevent_listener( listener ) ) );

		if ( listener_it == event_listeners_.end() )
		{
			event_listeners_.push_back( listener );
			return event_listeners_.back();
		}

		return *listener_it;
	}

	void uengine_interface::remove_event_listener( uevent_listener* listener )
	{
		std::remove_if( event_listeners_.begin(), event_listeners_.end(), remove_uevent_listener( listener ) );
	}
}
